package org.beynet.docs.auth;

import java.io.IOException;
import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

import org.beynet.docs.session.Session;
import org.beynet.docs.session.SessionImpl;

public class GDocsLoginModule implements LoginModule {

    private Subject subject;
    @SuppressWarnings("unused")
    private Map<String, ?> sharedStates;
    @SuppressWarnings("unused")
    private Map<String, ?> options;
    private CallbackHandler callbackHandler;
    private Session session;
    private String userName;
    private char[] password;
    private boolean loginSucceeded = false ;
    private GSyncPrincipal docsPrincipal = null ;
    private boolean commitSucceeded;

    public boolean abort() throws LoginException {
        userName = null ;
        cleanPasswd();
        if (commitSucceeded==true) {
            logout();
        }
        commitSucceeded = loginSucceeded = false ;
        return true;
    }

    public boolean commit() throws LoginException {
        if (loginSucceeded==false) return false;
        docsPrincipal = new GSyncPrincipal(userName, session);
        if (!subject.getPrincipals().contains(docsPrincipal)) subject.getPrincipals().add(docsPrincipal);
        cleanPasswd();
        commitSucceeded = true ;
        return(true);
    }

    public void initialize(Subject subject, CallbackHandler callbackHandler,
            Map<String, ?> sharedStates, Map<String, ?> options) {
        this.callbackHandler = callbackHandler;
        this.subject = subject ;
        this.sharedStates = sharedStates ;
        this.options = options ;
    }

    /**
     * return true as this LoginModule must not be ignored
     */
    public boolean login() throws LoginException {
        if (this.callbackHandler==null) throw new LoginException("No call back handler provided");
        Callback[] callbacks = new Callback[2];
        callbacks[0] = new NameCallback("user name: ");
        callbacks[1] = new PasswordCallback("password: ", false);
        //register callback into callback handler
        try {
            callbackHandler.handle(callbacks);
        } catch (Exception e) {
            throw new LoginException("Error :"+e.toString());
        }
        userName = ((NameCallback)callbacks[0]).getName();
        // retrieve password
        // -----------------
        char[] tmpPasswd = ((PasswordCallback)callbacks[1]).getPassword();
        if (tmpPasswd==null) tmpPasswd = new char[0];
        password = new char[tmpPasswd.length];
        System.arraycopy(tmpPasswd, 0,
                password, 0, tmpPasswd.length);
        ((PasswordCallback)callbacks[1]).clearPassword();
        
        // try do login
        session = new SessionImpl();
        try {
            session.doAuthentication(userName,password);
        } catch (IOException e) {
            cleanPasswd();
            throw new LoginException("bad user or password");
        }
        loginSucceeded = true ;
        
        return(true);
    }
    
    protected void cleanPasswd() {
        if (password!=null) {
            for (int i=0;i<password.length;i++) {
                password[i] = ' ';
            }
            password = null;
        }
    }

    public boolean logout() throws LoginException {
        subject.getPrincipals().remove(this.docsPrincipal);
        loginSucceeded = commitSucceeded = false ;
        docsPrincipal = null ;
        return(true);
    }

    public static final String applicationName ="fresnes-gsync-v1";
    
}
